Introduction ============ TVGraph is designed as a simple add in for any TurboVision application written for Turbo Pascal 6.0, Turbo Pascal 7.0, Borland Pascal 7.0 and Borland C++ 3.1. It performs two major functions, firstly it intercepts all necessary functions to allow your entire application to perform effectively unchanged - but in graphics mode. The other major function is the provision of an infrastructure for performing graphics oriented functions in a safe and transparent manner, including the provision of some graphic primitives and objects. Writing a TVG Application Just Graphics Mode ================== To enable graphics mode in an existing application written for Turbovision, just use the object gApp instead of TApplication to base your application object on, then pass a parameter to the constructor on initialising that application object. e.g. PASCAL : pMyapplication:=New(tMyApplication,.Init(smVGA640x480x16)); C++ : class TVDemo : public TApplication (as normal) then int TVGStartMode = smVGA640x480x16; int main(int argc, char **argv) { ... as normal } The graphics modes are smVGA320x200x16 smVGA640x200x16 smVGA640x350x16 smVGA640x480x16 smVesa800x600x16 smVesa1024x768x16 smVesa1280x1024x16 with the following modifiers smUseBGIInterface - initialises graphics using BGI smUseTVGInterface - initialises graphics using BIOS/VESA Note that only smVesa800x600x16 and below work in version 1.2, and smVGA320x200x16 doesn't work in BGI mode. Support for higher resolutions and mouse support in Vesa modes will be in version 1.3 . Note for C++ users - ensure you #include just after any point you normally would #include . PASCAL : The modified Application object gApp has the following interface : pgApp = ^gApp; gApp = object(TProgram) GrMode:word; (* Graphics mode of application *) Prodable:boolean; (* Prodable is TRUE for continuous updating *) constructor Init(Mode:word); destructor Done; virtual; procedure SetScreenMode(Mode: Word); procedure Idle; virtual; {$IFDEF Ver70} procedure Cascade; procedure DosShell; procedure GetTileRect(var R: TRect); virtual; procedure HandleEvent(var Event: TEvent); virtual; procedure Tile; procedure WriteShellMsg; virtual; {$ENDIF} end; CPP : The normal TProgram class has had several methods modified to allow for the TVGraph modes. A global byte (boolean) Prodable controls if TProgram.Idle continuously updates graphics views (prods). Add some graphics ================= To add some graphics to your application, you must use gWindow and gView for all windows and views that have graphics. These are decendants of TWindow and TView but with some modifications, firstly the gWindow. PASCAL : pgWindow = ^gWindow; gWindow = object(TWindow) constructor Init(var Bounds: TRect; ATitle: TTitleStr; ANumber: Integer); procedure ChangeBounds(var Bounds: TRect); virtual; procedure HandleEvent(var Event:TEvent);virtual; procedure GraphDraw; procedure GraphClear; procedure GraphProd; end; CPP : public: gWindow( const TRect& bounds, const char *aTitle, short aNumber); ~gWindow(); virtual void handleEvent(TEvent& event); virtual void changeBounds( const TRect& bounds ); void GraphDraw(); void GraphClear(); void GraphProd(); }; The handleevent routine has been modified to take care of the new graphics oriented events, and three methods have been added for graphics functionality. Each of the Graph methods calls the corresponding method in all gViews within that window. ChangeBounds has been over-ridden to allow for correct handling of moving graphics windows. Now for the gView : PASCAL : pgView = ^gView; gView = object(TView) GraphWindowId:byte; (* The graphics window Id for this view *) DisplayList:TCollection; (* List of gObjects constructor Init(Var Bounds:Trect); destructor Done;virtual; procedure GraphProd; procedure GraphDraw; procedure GraphClear; procedure Draw; virtual; procedure HandleEvent(var Event:TEvent); virtual; procedure ChangeBounds(var Bounds: TRect); virtual; procedure Insert(P:pgObject); procedure Delete(P:pgObject); end; CPP : class gView : public TView { public: gView( const TRect& bounds ); ~gView(); void Insert(gObject *P); void Delete(gObject *P); virtual void GraphProd(); virtual void GraphDraw(); void GraphClear(); virtual void draw(); virtual void handleEvent(TEvent& event); virtual void changeBounds( const TRect& bounds ); byte GraphWindowId; TNSCollection *DisplayList; }; The new data elements included here are the GraphWindowId value which keeps track of the graphics ID of this view. (See section on graphics ID for full explanation). The most important item for drawing graphics is the DisplayList collection. This is a normal Turbovision collection of gObjects. You can easily insert gObjects using Insert and Delete methods, just as you would insert and delete other views into parent views. The GraphClear method calls high speed horizontal line routines to clear the graphics view. The GraphDraw and GraphProd routines call corresponding routines for each gObject in the DisplayList. The gObject and it's decendants will be explained in the gObject section. Note - you should not override the Draw routines in decendants of gView as the last call inside any gView needs to be to GraphDraw. You can do it but be careful. To insert graphics into a gView just create an instance of a gObject and use the insert method of the parent gView. e.g. PASCAL : constructor MygWindow.Init; var R : TRect; GV : pgView; PB : pBox; begin R.Assign(0, 0, 34, 12); TWindow.Init(R, 'Box', wnNoNumber); GetExtent(R); R.Grow(-1,-1); GV:=New(pgView,Init(R)); PB:=New(pStyx,Init(5000,2000,8000,6000,14)); GV^.Insert(PB); (* Insert gObject into gView *) Insert(GV); (* insert gView into gWindow *) end; C++ : MygWindow::MygWindow() : gWindow( TRect(0, 0, 34, 12), "Box", wnNoNumber ), TWindowInit( &TStyxDemo::initFrame ) { gView *GV; gBox *PB; TRect r(getExtent()); r.grow(-1, -1); GV=new gView(r); PB=new gbox(5000,2000,8000,6000,14); GV->Insert(STYX); // Insert gObject into gView insert(GV); // Insert gView into gWindow } Now TVGraph will continually keep that graphic displayed and you need only call the gViews's GraphDraw and GraphClear routines when you change the objects and you wish to update them. Add some moving graphics ======================== The GraphProd method in the gWindow and gView implement a continuously updating graphic system. By setting the Prodable flag to true in the gApp decendant, the idle method will send a message to all gWindows and therefore to all gViews and eventually to all gObjects asking them to update there status. This prod routine need do nothing more than change the representation of the object - TVGraph will automatically call GraphProd and then update the screen by calling GraphDraw. See STYX.PAS and/or STYX.CPP for a full moving graphic example. The Graphics ID system explained ================================ TVGraph keeps an internal map of each virtual character position on the screen. All "text" positions on the screen are given the graphics id of 0. Each new gView you create automatically gets a unique graphics id (between 1 and 254). Whenever the gView draws itself - it does two things; first it updates the map to reflect it's graphics id, and second it draws all graphics associated with that view and it's graphics id. Thus (as can be seen in styx) no graphics from one view will write into anothers space and also no graphics will overwrite any menus or other standard TurboVision objects. You shouldn't have to worry about graphics IDs, but it is useful to know what they do. gObject - Object Oriented Graphics ================================== There are three graphics objects in this release the gLine, gBox and the root gObject. This will be extending with version 1.3. In PASCAL the are : the root graphics object : pgObject = ^gObject; gObject = object(TObject) Color:byte; constructor Init(C:byte); procedure GraphDraw(R:TRect); virtual; procedure GraphProd; virtual; procedure ChangeColor(C:Byte); end; graphics line object pgLine = ^gLine; gLine = object(gObject) Xa,Ya,Xb,Yb:integer; constructor Init(X1,Y1,X2,Y2:integer;C:byte); procedure GraphDraw(R:TRect); virtual; procedure ChangeEnds(X1,Y1,X2,Y2:integer); procedure ChangeEndsCopy(S:gLine); procedure DeltaEnds(X1,Y1,X2,Y2:integer); end; graphics box object pgBox = ^gBox; gBox = object(gLine) procedure GraphDraw(R:TRect); virtual; end; in CPP : class gObject : public TObject { public: gObject(void) { Color=0; } gObject(byte C) { Color=C; }; virtual void GraphDraw(TRect& R) = 0; virtual void GraphProd() { }; void ChangeColor(byte C) { Color=C; }; byte Color; }; class gLine : public gObject { public: gLine(void) : gObject(0) {Xa=Xb=Ya=Yb=0; }; gLine(int X1,int Y1,int X2,int Y2,byte C); virtual void GraphDraw(TRect& R); void ChangeEnds(int X1,int Y1,int X2,int Y2); void ChangeEnds(gLine S); void DeltaEnds(int X1,int Y1,int X2,int Y2); int Xa,Ya,Xb,Yb; }; class gBox : public gLine { public: gBox(void) : gLine() {}; gBox(int X1,int Y1,int X2,int Y2,byte C); virtual void GraphDraw(TRect& R); }; These three graphics objects (and the gStyx in the demo) are simple examples of object oriented graphics under TVGraph. The two key things a gObject needs to be able to do are : draw itself, and prod itself, by explanation here is the gLine.GraphDraw. PASCAL : procedure gLine.GraphDraw(R:TRect); var X1,Y1,X2,Y2:integer; begin GlobalToPhysical(R,Xa,Ya,X1,Y1); GlobalToPhysical(R,Xb,Yb,X2,Y2); DrawLine(X1,Y1,X2,Y2,Color); end; C++ void gLine::GraphDraw(TRect& R) { int X1,Y1,X2,Y2; GlobalToPhysical(R,Xa,Ya,X1,Y1); GlobalToPhysical(R,Xb,Yb,X2,Y2); DrawLine(X1,Y1,X2,Y2,Color); } All gObjects must represent themselves for drawing in a graphics space 10000 units wide and 7500 units high. (Defined by constants GraphMaxX and GraphMaxY). Then to draw itself, it is passed the rectangle that TVGraph assumes relates to that virtual co-ordinate system - allowing you to scale if you so wish. To assist you in drawing, the following two primitives are provided : PASCAL : procedure DrawLine(X1,Y1,X2,Y2:integer;Color:word); (* Draw a Line in screen co-ordinates of color Color *) procedure GlobalToPhysical(R:TRect;X0,Y0:integer;var X,Y:integer); (* Translate 10000x7500 co-ordinate in R to physical co-ordinate *) CPP : extern void cdecl DrawLine(ushort x1, ushort y1,ushort x2,ushort y2, ushort color); // Draw a Line in screen co-ordinates of color Color extern void cdecl GlobalToPhysical(TRect R,int X0,int Y0,int *X,int *Y); // Translate 10000x7500 co-ordinate in R to physical co-ordinate The Drawline primitive draws a line from X1,Y1 to X2,Y2 in screen co-ordinates in Color. It takes account of graphics ids and also of the mouse cursor. GlobalToPhysical converts virtual co-ordinates X0,Y0 to screen co-ordinates X,Y assuming a text rectangle of R. NOTES FOR ALL USERS =================== 1. The current release of MOUSE.COM (9.00) doesn't generate a mouse cursor in non-VGA resolutions. 2. The next modifications to the product are as follows : - Some minor - but annoying bug fixes Version 1.3 - More gObjects (High priority) Version 1.3 - Add full BGI support (High priority) Version 2.0 (any BGI writers out there want to write a custom TVGRAPH.BGI for royalty - contact me) - Add > 800x600x16 support - i.e. break the 64k barrier. (Medium Priority) Version 1.3 - Add 256 colour support (Medium Priority) Version 2.0 - Fix mouse support in hires (Low priority) Version 1.4? 3. WHEN I was 2 months out for this version - but i'll give it another go Version 1.3 .. September ? Version 1.4 .. ? Version 2.0 .. Early 1994 ? Cause I know how people like wrong dates more than none at all NOTES FOR C++ USERS =================== 1. Copy TVGRAPH.LIB into the same directory as TV.LIB and TVG.H into the same directory as TV.H. 2. Just by inserting TVGRAPH.LIB before TV.LIB in your link command your application will run in graphics mode with NO modifications. To use graphics objects you will need to include TVG.H in the source modules that need it. 3. The default value int TVGStartMode=smVGA640x480x16; is declared in TVGRAPH.LIB, if you wish to override this you need to insert a redeclaration of the same variable in the first module in the link statment (normally just before main()). 4. You may wish to turn of duplicate symbol warnings in your link stage as many of TVGRAPH's methods are overrides of TV.LIB methods. 5. I don't have access to a VESA.BGI for BC 3.1++ (and the one from BP 7 doesn't work) so although support is there for VESA 800x600 BGI mode it will not work as distributed. The vesa16 variable if declared and linked before the internal one will be accessed by the BGI initialisation part of TVGRAPH. UPDATES THIS VERSION ==================== 1.21 - Added more advanced cursor routines to stop the cursor disappearing. Uses timer tick ($1C).